use std::{collections::HashMap, fmt::Debug};
use std::borrow::BorrowMut;
use std::future::Future;

use async_session::{async_trait, MemoryStore, Session as AsyncSession, SessionStore};
use axum::{
    body::Body,
    extract::{FromRequest, RequestParts},
    headers::Cookie,
    http::{Request, StatusCode},
    middleware::Next,
    response::IntoResponse,
    Extension,
};
use axum::headers::HeaderValue;
use serde::{Deserialize, Serialize};
use tracing::info;
use super::Result;
use errors::AppError;

use crate::{errors, internal_error};

pub async fn session_middleware(
    mut request: Request<Body>,
    next: Next<Body>,
) -> Result<impl IntoResponse> {
    // let request = buffer_request_body(request).await?;
    info!("{:#?}", request);
    info!("{:#?}", request.uri().path());
    match request.headers().get("cookie") {
        None => {
            // 当cookie不存在时避免出错，手动插入一个cookie值
            request.headers_mut().insert("cookie", "1=2".parse().unwrap());
        }
        Some(_) => {}
    }

    Ok(next.run(request).await)
}

#[derive(Debug, Clone)]
pub struct Session {
    store: MemoryStore,
    pub cookie_name: String,
}

impl Session {
    pub fn new(cookie_name: &str) -> Self {
        Self {
            store: MemoryStore::new(),
            cookie_name: cookie_name.to_string(),
        }
    }
    pub async fn get_session_data(&self, cookie: Cookie) -> Result<SessionData> {
        if let Some(cookie_value) = cookie.get(self.cookie_name.as_str()) {
            let session = self
                .store
                .load_session(cookie_value.to_string())
                .await?;
            if let Some(session) = session {
                if let Some(session_data) = session
                    .get::<SessionData>(self.cookie_name.as_str()) {
                    Ok(session_data)
                } else {
                    Err(AppError::from_str(format!("cookie_name: {} not found", self.cookie_name).as_str(), 401))
                }
            } else {
                Err(AppError::from_str(format!("cookie_value: {} not found", cookie_value).as_str(), 401))
            }
        } else { Err(AppError::from_str("cookie_value not set", 401)) }
    }
    pub async fn set_session_data(&self, session_data: SessionData) -> String {
        let mut session = AsyncSession::new();
        session
            .insert(self.cookie_name.as_str(), session_data)
            .unwrap();
        let cookie_value = self.store.store_session(session).await.unwrap().unwrap();
        format!("{}={}", self.cookie_name, cookie_value)
    }
    pub async fn clear(&self, cookie: Cookie) -> Result<()> {
        if let Some(cookie_value) = cookie.get(self.cookie_name.as_str()) {
            let session = self
                .store
                .load_session(cookie_value.to_string())
                .await?;
            if let Some(session) = session {
                match self.store.destroy_session(session).await {
                    Ok(_) => { Ok(()) }
                    Err(_) => { Ok(()) }
                }
            } else {
                Ok(())
            }
        } else { Ok(()) }
    }
}

#[async_trait]
impl<B> FromRequest<B> for Session
    where
        B: Send,
{
    type Rejection = AppError;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self> {
        let Extension(session) = Extension::<Session>::from_request(req)
            .await?;

        let store = session.store.clone();

        Ok(Self {
            store,
            cookie_name: session.cookie_name,
        })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SessionData {
    pub data: HashMap<String, String>,
}

impl SessionData {
    pub fn new() -> Self {
        Self {
            data: HashMap::new(),
        }
    }

    pub fn insert(&mut self, k: String, v: String) {
        self.data.insert(k, v);
    }

    pub fn get(&self, k: &str) -> Result<String> {
        if let Some(v) = self.data.get(k) {
            Ok((*v).clone())
        } else {
            Err(AppError::from_str("session_data is empty", 401))
        }
    }
}

impl Default for SessionData {
    fn default() -> Self {
        Self::new()
    }
}
